package com.xuhuan.demo.ftp;

import cn.hutool.core.date.DateUtil;
import com.xuhuan.demo.ftp.config.FtpConfig;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.*;
import java.util.Date;
import java.util.UUID;

/**
 * @author huan.xu
 * @date 2021/11/24 13:34
 * @description ftp工具类
 */
@Slf4j
@Component
public class FtpUtil {

    private FtpUtil() {
    }

    private static FtpConfig ftpConfig;

    @Autowired
    public void setFtpConfig(FtpConfig ftpConfig) {
        FtpUtil.ftpConfig = ftpConfig;
    }


    /**
     * 获取ftp连接，并登录ftp服务器
     *
     * @return
     */
    public static FTPClient getConnect() {
        FTPClient ftpClient = new FTPClient();
        try {
            ftpClient.setConnectTimeout(1000 * 30);//设置连接超时时间
            ftpClient.setControlEncoding("utf-8");//设置ftp字符集
            ftpClient.enterLocalPassiveMode();//设置被动模式，文件传输端口设置

            // 连接FTP服务器
            ftpClient.connect(ftpConfig.getHost(), ftpConfig.getPort());
            // 登录
            ftpClient.login(ftpConfig.getUsername(), ftpConfig.getPassword());
            int replyCode = ftpClient.getReplyCode();
            if (!FTPReply.isPositiveCompletion(replyCode)) {
                log.error("登录FTP失败，登录返回码[{}]", replyCode);
                throw new RuntimeException("登录FTP失败");
            }
            //设置上传文件的类型为二进制类型
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
        } catch (Exception e) {
            log.error("连接或者登录ftp错误，错误信息：{}", ExceptionUtils.getFullStackTrace(e));
            throw new RuntimeException("连接或者登录ftp错误");
        }
        log.info("登录ftp服务器成功");
        return ftpClient;
    }

    /**
     * 向FTP服务器上传文件
     *
     * @param filePath FTP服务器文件存放路径。例如分日期存放：/2015/01/01。文件的路径为basePath+filePath
     * @param filename 上传到FTP服务器上的文件名
     * @param input    本地要上传的文件的 输入流
     * @return 成功返回true，否则返回false
     */
    public static void uploadFile(String filePath, String filename, InputStream input) throws Exception {
        long startTime = System.currentTimeMillis();
        FTPClient ftpClient = getConnect();

        //切换到上传目录
        if (!ftpClient.changeWorkingDirectory(ftpConfig.getPathName() + filePath)) {
            log.info("开始切换目录");
            //如果目录不存在创建目录
            String[] dirs = filePath.split("/");
            String tempPath = ftpConfig.getPathName();
            for (String dir : dirs) {
                if (null == dir || "".equals(dir)) {
                    continue;
                }
                tempPath += "/" + dir;
                if (!ftpClient.changeWorkingDirectory(tempPath)) {
                    if (!ftpClient.makeDirectory(tempPath)) {
                        log.error("创建上传目录失败");
                        throw new RuntimeException("创建上传目录失败");
                    } else {
                        ftpClient.changeWorkingDirectory(tempPath);
                    }
                }
            }
        }
        //上传文件
        if (!ftpClient.storeFile(filename, input)) {
            log.error("上传文件失败");
            throw new RuntimeException("上传文件失败");
        }
        if (input != null) {
            input.close();
        }
        if (ftpClient != null) {
            ftpClient.logout();
            ftpClient.disconnect();
        }
        long endTime = System.currentTimeMillis();
        log.info("上传文件到ftp成功，路径为[{}/{}]，花费时间:[{}]ms,[{}]s", filePath, filename, (endTime - startTime), (endTime - startTime) / 1000);

    }

    /**
     * 向FTP服务器上传文件
     *
     * @param fileType 文件类型，例如:.txt
     * @param filePath 要上传的文件路径
     * @throws Exception
     */
    public static void uploadFile(String fileType, String filePath) throws Exception {
        uploadFile(generateFtpDir(null), generateFtpFileName(fileType), new FileInputStream(filePath));
    }

    /**
     * Description: 从FTP服务器下载文件 <没有经过改造>
     *
     * @param host       FTP服务器hostname
     * @param port       FTP服务器端口
     * @param username   FTP登录账号
     * @param password   FTP登录密码
     * @param remotePath FTP服务器上的相对路径
     * @param fileName   要下载的文件名
     * @param localPath  下载后保存到本地的路径
     * @return
     */
    public static boolean downloadFile(String host, int port, String username, String password, String remotePath,
                                       String fileName, String localPath) {
        boolean result = false;
        FTPClient ftp = new FTPClient();
        try {
            int reply;
            ftp.connect(host, port);
            // 如果采用默认端口，可以使用ftp.connect(host)的方式直接连接FTP服务器
            ftp.login(username, password);// 登录
            reply = ftp.getReplyCode();
            if (!FTPReply.isPositiveCompletion(reply)) {
                ftp.disconnect();
                return result;
            }
            ftp.changeWorkingDirectory(remotePath);// 转移到FTP服务器目录
            FTPFile[] fs = ftp.listFiles();
            for (FTPFile ff : fs) {
                if (ff.getName().equals(fileName)) {
                    File localFile = new File(localPath + "/" + ff.getName());

                    OutputStream is = new FileOutputStream(localFile);
                    ftp.retrieveFile(ff.getName(), is);
                    is.close();
                }
            }

            ftp.logout();
            result = true;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (ftp.isConnected()) {
                try {
                    ftp.disconnect();
                } catch (IOException ioe) {
                }
            }
        }
        return result;
    }


    /**
     * 根据日期生成日期格式目录,例如：/2015/01/21
     *
     * @param date
     * @return
     */
    public static String generateFtpDir(Date date) {
        if (date == null) {
            date = new Date();
        }
        String dir = DateUtil.format(date, "yyyy/MM/dd");
        if (StringUtils.isNoneBlank(dir)) {
            if (!dir.startsWith("/")) {
                dir = "/" + dir;
            }
        }
        return dir;
    }

    /**
     * 生成ftp文件名称，
     *
     * @param fileType 文件类型名称
     * @return
     */
    public static String generateFtpFileName(String fileType) {

        String uuid = UUID.randomUUID().toString().replace("-", "");
        if(ftpConfig.getUuidLen()==0){
            log.error("uuid长度未配置");
            throw new RuntimeException("uuid长度未配置");
        }
        if(ftpConfig.getUuidLen()<10){
            log.error("uuid长度最低10位");
            throw new RuntimeException("uuid长度最低10位");
        }
        StringBuilder fullFileName = new StringBuilder("");
        fullFileName.append(uuid.substring(0, ftpConfig.getUuidLen()-1));
        if (!fileType.startsWith(".")) {
            fullFileName.append(".");
        }
        fullFileName.append(fileType);
        return fullFileName.toString();
    }
}
